#include"stdafx.h"
#include"ObjectBattler.h"
#include"BattleManager.h"
#include"GameManager.h"
#include"Database.h"

double Damage::criticalRate = 2.0;
double Damage::defendRate = 0.2;

int BattleManager::level;
int BattleManager::score;
int BattleManager::time;
int BattleManager::recover;
int BattleManager::hurt;
int BattleManager::beHurt;
double BattleManager::difficulty;
int BattleManager::mhmp;

Battle* BattleManager::battle;
Bitmap* BattleManager::passableBitmap;
ObjectActor* BattleManager::actor;
vector<ObjectEnemy> BattleManager::enemies;
queue<RequestRecord> BattleManager::requestQueue;
vector<OperListener> BattleManager::operListeners;

RequestRecord::RequestRecord(ObjectBattler* obj, RequestType rt, int dt) :
	object(obj), rType(rt) {data.push_back(dt);};

RequestRecord::RequestRecord(ObjectBattler* obj, RequestType rt, vector<int> dt) :
	object(obj), rType(rt), data(dt) {};

RequestRecord::RequestRecord(ObjectBattler* obj, RequestType rt, vector<int> dt, 
	vector<AnimationRequestData*> aqdt) :
	object(obj), rType(rt), data(dt), aniReqData(aqdt) {};

OperListener::OperListener(ObjectBattler* obj, RequestRecord rec) :
	object(obj), record(rec) { finished = false; };

Damage::Damage():object(NULL){}
Damage::Damage(ObjectBattlerBase* object, UsableItem* item, ObjectBattlerBase* target, double rate) {
	this->Damage::Damage(dynamic_cast<ObjectBattler*>(object), item, dynamic_cast<ObjectBattler*>(target));
}
Damage::Damage(ObjectBattler* object, UsableItem* item, ObjectBattler* target, double rate) :
	object(object), item(item), target(target) {
	processDamage(rate);
}

int Damage::getDamageValue(DamageType type) { return damageValue[type]; }
Damage::HitType Damage::getHitResult() { 
	return hitResult; 
}
UsableItem* Damage::getItem() { return item; }
ObjectBattler* Damage::getObject() { return object; }
ObjectBattler* Damage::getTarget() { return target; }

Damage Damage::makeReverseDamage() {
	Damage rev = Damage();
	rev.object = object; rev.target = object;
	rev.item = item; rev.hitResult = hitResult;
	for (int i = 0; i < 6; i++) rev.damageValue[i] = 0;
	rev.damageValue[DamageType::HPRecover] = damageValue[DamageType::HPAbsorb];
	rev.damageValue[DamageType::MPRecover] = damageValue[DamageType::MPAbsorb];
	return rev;
}

bool Damage::isActiveDamage() { return activeDamage; }
bool Damage::isActiveReverseDamage() { return activeReverseDamage; }

void Damage::processDamage(double rate) {
	for (int i = 0; i < 6; i++) damageValue[i] = 0;
	activeDamage = activeReverseDamage = false;
	hitResult = None;
	for (int i = 0; i < item->usingEffects.size(); i++) {
		UsingEffect* ue = &item->usingEffects[i];
		double r; hitResult = Hit;
		if (ue->hit100);
		else if (r = (rand() % 100) / 100.0 >= object->hitRate())  
			hitResult = Miss;
		else if (r = (rand() % 100) / 100.0 < target->missRate())  
			hitResult = Miss;
		if (hitResult == Hit) {
			double value = ue->baseValue;
			for (int i = 0; i < System::BaseParamCount; i++) {
				value += object->param(i)*ue->paramsRate[i];
				value += target->param(i)*ue->targetParamsRate[i];
			}
			if (!ue->hit100 && target->isDefending()) value *= defendRate;
			if(ue->dispersed>0)
				value *= 1+(rand()%(ue->dispersed<<1) - ue->dispersed)/100.0;
			if (ue->critable && ((r = (rand() % 100) / 100.0) < object->critical())) {
				hitResult = Crit; value *= criticalRate;
			}
			damageValue[ue->damageType] += value*rate;
		}	
	}
	if (hitResult != None) {
		for (int i = 0; i < 6; i++) damageValue[i] = max(0, damageValue[i]);
		for (int i = 0; i < 6; i++) if (damageValue[i] > 0) { activeDamage = true; break; }
		for (int i = 4; i < 6; i++) if (damageValue[i] > 0) { activeReverseDamage = true; break; }
		if (hitResult == Miss) activeDamage = true;
	}
}

void BattleManager::setupBattle(int id, int level) {
	GameManager::inBattle = true;
	BattleManager::level = level;
	battle = &battles[id]; 
	initializeScore();
	createMap();
	createBattlers();
}
void BattleManager::endBattle() {
	actor->clearCoolDownList();
	GameManager::inBattle = false;
}
void BattleManager::updateRecord() {
	GameManager::setBattleScoreRecord(battle->index, level, getScore(), getJudge());
	Armor* armor = &armors[System::LevelPassReward[level]];
	if (GameManager::hasItem(armor) <= 0 && GameManager::isBattleLevelPassed(level))
		GameManager::gainItem(armor, 1);
}

void BattleManager::initializeScore() {
	score = time = recover = beHurt = hurt = 0;
	difficulty = battle->difficluty; mhmp = 1;
}

int BattleManager::getScore() { 
	return max(0,score + (time + hurt * 3)*(difficulty + exp(-time / 5000.0) - 1 - beHurt*0.1 / (mhmp)));
}
void BattleManager::addScore(int val) { score += val; }
void BattleManager::addRecover(int val) { recover += val; }

int BattleManager::getLevel() { return level; }
int BattleManager::getTime() { return time; }
int BattleManager::getRecoverPoint() { return recover; }
int BattleManager::getHurtPoint() { return hurt; }
int BattleManager::getBeHurtPoint() { return beHurt; }
double BattleManager::getDifficulty() { return difficulty; }
int BattleManager::getAdditionalScore() { return score; }
int BattleManager::getJudge() { 
	return battleResult() == Lose ? 6 : calcJudgeLevel(); }
int BattleManager::getGold() { 
	return getScore()*difficulty / (battleResult() == Lose ? 50 : 2);
}


int BattleManager::calcJudgeLevel() {
	double judgeScore = getScore();
	if (beHurt == 0 && recover == 0) judgeScore *= 15;
	else if (beHurt == 0) judgeScore *= 5;
	else if (recover == 0) judgeScore *= 2;
	else judgeScore-=(beHurt+recover/25)/2;
	if (judgeScore > 166666) return 0;
	if (judgeScore > 66666) return 1;
	if (judgeScore > 23333) return 2;
	if (judgeScore > 10000) return 3;
	if (judgeScore > 6666) return 4;
	return 5;
}

bool BattleManager::passable(int x, int y) {
	return x >= 0 && x < passableBitmap->getWidth() &&
		y >= 0 && y < passableBitmap->getHeight() &&
		passableBitmap->getData(x, y) == 'T';
}
void BattleManager::createMap() {
	passableBitmap = Bitmap::getBitmap(battle->battleField.passableFileName);
}
void BattleManager::createBattlers() {
	createPlayer(); createEnemies();
}
void BattleManager::createPlayer() {
	actor = GameManager::getLeader();
	actor->transferTo(battle->battleField.playerCreatePos[0],
		battle->battleField.playerCreatePos[1]);
	mhmp = actor->mhp() + actor->mmp();
}
void BattleManager::createEnemies() {
	enemies.clear();
	//Rect* block = &battle->battleField.enemyCreateBlock;
	BattleEnemy* enemy;
	Rect* block; int cnt = 0, x, y;
	for (int i = 0; i < battle->battleEnemies.size(); i++) {
		enemy = &battle->battleEnemies[i];
		block = &enemy->position;
		for (int j = 0; j < enemy->count; j++) {
			enemies.push_back(ObjectEnemy(enemy->enemyId, level + 1));
			do {
				x = block->x + (rand() % block->width);
				y = block->y + (rand() % block->height);
			} while (!passable(x, y));
			enemies[cnt++].transferTo(x, y);
		}	
	}
}

ObjectActor* BattleManager::getActor() { return actor; }
ObjectEnemy* BattleManager::getEnemy(int id) { return &enemies[id]; }
int BattleManager::getEnemyCount() { return enemies.size(); }
Battle* BattleManager::getBattle() { return battle; }

int BattleManager::dist2D2(ObjectBattler* battler1, ObjectBattler* battler2) {
	int dx = battler1->getX() - battler2->getX();
	int dy = battler1->getY() - battler2->getY();
	return dist2D2(dx, dy);
}
int BattleManager::dist3D2(ObjectBattler* battler1, ObjectBattler* battler2) {
	int dx = battler1->getX() - battler2->getX();
	int dy = battler1->getY() - battler2->getY();
	int dz = battler1->getZ() - battler2->getZ();
	return dist3D2(dx, dy, dz);
}
int BattleManager::dist2D2(int x1, int y1, int x2, int y2) {
	return dist2D2(x1 - x2, y1 - y2);
}
int BattleManager::dist3D2(int x1, int y1, int z1, int x2, int y2, int z2) {
	return dist3D2(x1 - x2, y1 - y2, z1 - z2);
}
int BattleManager::dist2D2(int dx, int dy) { return dx*dx + dy*dy; }
int BattleManager::dist3D2(int dx, int dy, int dz) { return dx*dx + dy*dy + dz*dz; }

vector<ObjectBattler*> BattleManager::getTargets(
	ObjectBattler* battler, Skill* skill) {
	vector<ObjectBattler*> targets;
	if (skill->target == System::TargetType::NoneTarget) return targets;
	if (skill->target == System::TargetType::Self) {
		targets.push_back(actor); return targets;
	}
	if (skill->target == System::TargetType::Enemies)
		targets = getEnemiesFor(battler);
	if (skill->target == System::TargetType::Friends)
		targets = getFriendsFor(battler);
	if (skill->target == System::TargetType::DeadFriends)
		targets = getFriendsFor(battler, true);

	vector<ObjectBattler*>::iterator eit;
	SkillRange* sr = &skill->range;
	int ex, ey, ez, bx, by, bz, dx, dy, dz, cnt = sr->count;
	bx = battler->getX() + sr->pos[0] * (battler->getDirection() ? -1 : 1);
	by = battler->getY() + sr->pos[1] * (battler->getDirection() ? -1 : 1);
	bz = battler->getZ();
	for (int i = targets.size() - 1; i >= 0; i--) {
		eit = targets.begin() + i;

		if (cnt == 0) { targets.erase(eit); continue; }
		ex = (*eit)->getX(); ey = (*eit)->getY(); ez = (*eit)->getZ();
		dx = (ex - bx)*(battler->getDirection() ? 1 : -1);
		dy = ey - by; dz = ez - bz;
		if (sr->ballRange && dist3D2(dx, dy, dz) <= sr->dist*sr->dist) { cnt--; continue; }
		if (sr->zRange == 0 || abs(dz) <= sr->zRange) {
			if (sr->wholeScreen) { cnt--; continue; }
			if (sr->wholeLine && abs(dy) <= sr->dist) { cnt--; continue; }
			if (dist2D2(dx, dy) < sr->dist*sr->dist) {
				if (sr->fullAngle) { cnt--; continue; }
				if (dx >= 0) { cnt--; continue; }
			}
		}
		targets.erase(eit);
	}
	return targets;
}
vector<ObjectBattler*> BattleManager::getTargets(
	ObjectBattler* battler, Item* item) {
	vector<ObjectBattler*> targets;
	if (item->target == System::TargetType::Friends ||
		item->target == System::TargetType::Self);
		targets.push_back(battler);
	return targets;
}
vector<ObjectBattler*> BattleManager::getEnemiesFor(ObjectBattler* battler) {
	vector<ObjectBattler*> res;
	if (battler->isActor())
		for (int i = 0; i < enemies.size(); i++) {
			ObjectBattler* ePtr = &enemies[i];
			if (!ePtr->isDead() && !ePtr->isHidden())res.push_back(ePtr);
		}
	if (battler->isEnemy()) res.push_back(actor);
	return res;
}
vector<ObjectBattler*> BattleManager::getFriendsFor(ObjectBattler* battler,bool deadOnly) {
	vector<ObjectBattler*> res;
	if (battler->isActor())
		if ((!deadOnly && !actor->isDead())) res.push_back(actor);
		else if ((deadOnly && actor->isDead())) res.push_back(actor);
	if (battler->isEnemy()) {
		for (int i = 0; i < enemies.size(); i++) {
			ObjectBattler* ePtr = &enemies[i];
			if (ePtr->isHidden()) continue;
			if (deadOnly && ePtr->isDead()) res.push_back(ePtr);
			else if(!deadOnly && !ePtr->isDead()) res.push_back(ePtr);
		}
	}
	return res;
}
void BattleManager::addOperListener(ObjectBattler* object, RequestRecord record) {
	operListeners.push_back(OperListener(object, record));
}
BattleManager::BattleResult BattleManager::battleResult() {
	if (tied()) return Tied;
	if (win()) return Win;
	if (lose()) return Lose;
	return None;
}
bool BattleManager::win() {
	for (int i = 0; i < enemies.size(); i++) if (!enemies[i].isDead()) return false;
	return true;
}
bool BattleManager::lose() {return actor->isDead();}
bool BattleManager::tied() { return win() && lose(); }

void BattleManager::dealDamage(ObjectBattler* battler, Skill* skill, ObjectBattler* target, double rate) { 
	Damage dam = Damage(battler, skill, target, rate);
	if (dam.isActiveDamage()) {
		if(dam.getHitResult()==Damage::HitType::Miss)
			target->requestOpers(missOper);
		else if (dam.getHitResult() == Damage::HitType::Crit)
			target->requestOpers(criticalOper);
		else
			target->requestOpers(skill->targetOper);
		target->dealDamage(dam);
		if (target == actor) {
			recover += dam.getDamageValue(Damage::DamageType::HPRecover);
			recover += dam.getDamageValue(Damage::DamageType::MPRecover);
			beHurt += dam.getDamageValue(Damage::DamageType::HPDamage);
		}
		if (battler == actor) {
			hurt += dam.getDamageValue(Damage::DamageType::HPDamage);
		}
		if (dam.isActiveReverseDamage()) 
			battler->dealDamage(dam.makeReverseDamage());
	}
}

void BattleManager::update() {
	if(GameManager::inBattle) time++;
	updateRequests();
	updateBattlers();
	updateOperListeners();
}
void BattleManager::updateBattlers() {
	actor->update();
	for (int i = 0; i < enemies.size(); i++)
		enemies[i].update();
	updateDirection();
	updateEnemiesAI();
}
void BattleManager::updateDirection() {
	vector<ObjectBattler*> enemies = getEnemiesFor(actor);
	if (enemies.size() <= 0) return;
	int minDist = dist2D2(actor, enemies[0]), minI = 0;
	for (int i = 1; i < enemies.size(); i++) {
		if (dist2D2(actor, enemies[i]) < minDist) minI = i;
	}
	if(!actor->isFixedDirection())
		actor->faceTo(enemies[minI]);
}
void BattleManager::updateEnemiesAI() {
	for (int i = 0; i < enemies.size(); i++) {
		if (enemies[i].isDead() || enemies[i].isDelaying()) continue;
		updateEnemyMoveAI(&enemies[i]);
		updateEnemyBattleAI(&enemies[i]);
	}
}
void BattleManager::updateEnemyMoveAI(ObjectEnemy* enemy) {
	int ax = actor->getX(), ay = actor->getY();
	int ex = enemy->getX(), ey = enemy->getY();
	Enemy* e = enemy->getEnemy();
	if (e->aiType == Enemy::AIType::None) return;
	switch (e->aiType) {
	case Enemy::AIType::Closer:
		if (e->aiParams[1] < 0 || dist2D2(ex, ey, ax, ay) <= e->aiParams[1] * e->aiParams[1]) {
			int tx = ax + (ax > ex ? -1 : 1)*(rand() % 20 + 10);
			int ty = ay + (ay > ey ? -1 : 1)*(rand() % 10 + 5);
			enemy->moveTo(tx, ty);
		}else
			switch (e->aiParams[2]) {
			case 1: enemy->clearTargetPosition(); break;// stop
			case 2: enemy->updateRandomMove(); break;// random
			}
		break;
	case Enemy::AIType::Idle: enemy->clearTargetPosition(); break;
	case Enemy::AIType::Random: enemy->updateRandomMove(); break;
	}
}
void BattleManager::updateEnemyBattleAI(ObjectEnemy* enemy) {
	if (enemy->isOpering() || actor->isOpering() || actor->isDead()) return;
	int ax = actor->getX(), ay = actor->getY();
	int ex = enemy->getX(), ey = enemy->getY();
	Enemy* e = enemy->getEnemy();
	if (e->aiType == Enemy::AIType::None) return;
	if (e->aiParams[0] < 0 || dist2D2(ex, ey, ax, ay) > e->aiParams[0]*e->aiParams[0]) return;
	Skill* skill;
	int loopCnt = 0;
	do { 
		skill = enemy->getRandomSkill(); loopCnt++;
		if (loopCnt >= 1000) break;
	} while (skill!=NULL && !enemy->isSkillUsable(skill));
	if (!getTargets(enemy, skill).empty()) enemy->useSkill(skill);
}
void BattleManager::updateRequests() {
	checkRequests();
	processRequests();
}
void BattleManager::updateOperListeners() {
	processOperListeners();
}
void BattleManager::checkRequests() {
	checkRequestsFor(actor);
	for (int i = 0; i < enemies.size(); i++)
		checkRequestsFor(&enemies[i]);
}
void BattleManager::processRequests() {
	while (!requestQueue.empty()) {
		processRequestsFor(&requestQueue.front());
		requestQueue.pop();
	}
}
void BattleManager::processOperListeners() {
	vector<OperListener>::iterator olit = operListeners.end();
	for (int i = operListeners.size() - 1; i >= 0; i--) {
		olit = (operListeners.begin() + i);
		processOperListener(&(*olit));
		olit = (operListeners.begin() + i);
		if (olit->finished)
			operListeners.erase(olit);
	}
}
void BattleManager::processOperListener(OperListener* listener) {
	if (!listener->object->isOpering()) {
		listener->finished = true;
		processRequestsFor(&listener->record);
	}
}
void BattleManager::checkRequestsFor(ObjectBattler* battler) {
	if (!battler->isAnyRequested()) return;
	Skill* skillR = battler->skillRequested();
	vector<AnimationRequestData*> aniTR = battler->animationsAtTargetRequested();
	int damage = battler->damageRequested();
	int toTarget = battler->requestedToTarget();

	if (skillR != NULL) {
		requestQueue.push(RequestRecord(battler, RequestRecord::Skill, skillR->index));
		battler->clearRequestedSkill();
	}
	if (!aniTR.empty()) {
		vector<int> data;
		data.push_back(battler->getOperingSkill()->index);
		//for (int i = 0; i < aniTR.size(); i++) aniReqData.push_back(aniTR[i]);
		requestQueue.push(RequestRecord(battler, RequestRecord::AnimationToTarget, data, aniTR));
		battler->clearRequestedAnimationsAtTarget();
	}
	if (damage >= 0) {
		vector<int> data;
		data.push_back(battler->getOperingSkill()->index);
		data.push_back(damage);
		requestQueue.push(RequestRecord(battler, RequestRecord::Damage, data));
		battler->clearRequestedDamage();
	}
	if (toTarget > 0) {
		vector<ObjectBattler*> targets = getEnemiesFor(battler);
		if (targets.size() > 0) {
			ObjectBattler* target = getEnemiesFor(battler)[0];
			if (toTarget == 1) {// move
				battler->moveTo(target->getX(), target->getY());
			}else if(toTarget==2){// transfer
				battler->transferTo(target->getX(), target->getY());
			}
		}
		battler->clearRequestedToTarget();
	}
	battler->clearRequestAny();
}
void BattleManager::processRequestsFor(RequestRecord* record) {
	ObjectBattler* battler = record->object;
	vector<int> params = record->data;
	vector<ObjectBattler*> targets;
	Skill* skill; double rate;

	switch (record->rType) {
		case RequestRecord::Skill:
			skill = &skills[params[0]];
			battler->requestOpers(skill->preSkillOper, skill);
			record->rType = RequestRecord::SkillMobilize;
			addOperListener(battler, *record);
			break;
		case RequestRecord::SkillMobilize:
			skill = &skills[params[0]];
			battler->requestOpers(skill->usingOper,skill);
			//addOperListener(battler, *record);
			break;
		case RequestRecord::Damage:
			skill = &skills[params[0]];
			rate = params[1] / 100.0;
			targets = getTargets(battler, skill);
			for (int i = 0; i<targets.size(); i++) {
				dealDamage(battler,skill,targets[i],rate);
			}
			break;
		case RequestRecord::AnimationToTarget:
			vector<AnimationRequestData*> aniReqParams = record->aniReqData;
			skill = &skills[params[0]];
			targets = getTargets(battler, skill);
			for (int i = 0; i < targets.size(); i++)
				for (int j = 0; j < aniReqParams.size();j++)
					targets[i]->requestAnimation(aniReqParams[j]);
			break;
	}
}